package com.yifeng.repo.base.utils.action.impl;

import com.gomcarter.frameworks.base.exception.CustomException;
import com.google.common.base.Strings;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.yifeng.repo.base.constant.BaseConstant;
import com.yifeng.repo.base.params.BizCacheParam;
import com.yifeng.repo.base.utils.action.BizCacheAction;
import com.yifeng.repo.base.utils.redis.worker.RedisCacheWorker;

import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * Created by daibing on 2023/8/26.
 */
public class BizCompositeCacheAction implements BizCacheAction {
    private final RedisCacheWorker redisCacheWorker;
    private final Cache<String, String> localCache;
    private final boolean enableLocalCache;
    private final String redisKeyPrefix;

    public BizCompositeCacheAction(BizCacheParam bizCacheParam, RedisCacheWorker redisCacheWorker) {
        this.redisCacheWorker = redisCacheWorker;
        this.enableLocalCache = bizCacheParam.isEnableLocalCache();
        this.redisKeyPrefix = bizCacheParam.getRedisKeyPrefix();
        this.localCache = enableLocalCache ? CacheBuilder.newBuilder()
                .initialCapacity(32)
                .maximumSize(bizCacheParam.getLocalMaximumSize())
                .concurrencyLevel(16)
                .expireAfterWrite(bizCacheParam.getLocalDurationSeconds(), TimeUnit.SECONDS)
                .build() : null;
    }

    @Override
    public String redisKeyPrefix() {
        return redisKeyPrefix;
    }

    @Override
    public String get(String key) {
        if (!enableLocalCache) {
            return getByRedis(key);
        }
        try {
            String value = localCache.get(key, () -> Optional.ofNullable(getByRedis(key)).orElse(BaseConstant.EMPTY));
            return Strings.isNullOrEmpty(value) ? null : value;
        } catch (ExecutionException e) {
            throw new CustomException(e);
        }
    }

    private String getByRedis(String key) {
        if (redisCacheWorker == null) {
            return null;
        }
        return redisCacheWorker.get(key);
    }

    @Override
    public void put(String key, long seconds, String value) {
        if (enableLocalCache) {
            localCache.put(key, value);
        }
        if (redisCacheWorker != null) {
            redisCacheWorker.put(key, seconds, value);
        }
    }
}
